/*
 * Copyright (C) 2011 TopCoder Inc., All Rights Reserved.
 */
package gov.nasa.pds.services.impl;

import gov.nasa.pds.entities.Loggable;
import gov.nasa.pds.services.DataSetProcessingException;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;

import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.PreparedStatementCreator;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.support.KeyHolder;

import com.topcoder.util.log.Level;
import com.topcoder.util.log.Log;

/**
 * <p>
 * The Helper class.
 * </p>
 *
 * @author TCSASSEMBLER
 * @version 1.0
 */
public final class Helper {
    /**
     * The default do nothing constructor.
     */
    private Helper() {
    }

    /**
     * Checks whether the given value is not empty (including the case that empty after trimming).
     *
     * @param jdbcTemplate the jdbc template
     * @param sql the sql
     * @param args the value of each row
     * @return the generated id
     */
    public static long insert(JdbcTemplate jdbcTemplate, final String sql, final Object... args) {
        KeyHolder keyHolder = new GeneratedKeyHolder();
        jdbcTemplate.update(
            new PreparedStatementCreator() {
                public PreparedStatement createPreparedStatement(Connection connection) throws SQLException {
                    PreparedStatement ps =
                        connection.prepareStatement(sql, new String[] {"id"});
                    int i = 0;
                    for (Object arg : args) {
                        i++;
                        ps.setObject(i, arg);
                    }
                    return ps;
                }
            },
            keyHolder);
        return keyHolder.getKey().longValue();
    }

    /**
     * Gets the keyword id. If not exists, then creates a new record.
     *
     * @param name the keyword name
     * @param jdbcTemplate the jdbc template
     * @return the keyword id
     */
    public static Long getKeywordId(String name, JdbcTemplate jdbcTemplate) {
        Long keywordId = null;
        List<Map<String, Object>> keywordIdList =
            jdbcTemplate.queryForList("select id from keyword where name = ?",
                new Object[] {name});
        if (keywordIdList.size() > 0) {
            keywordId = (Long) keywordIdList.get(0).get("id");
        }
        if (keywordId == null) {
            keywordId = Helper.insert(jdbcTemplate, "insert into keyword (name) values (?)",
                    new Object[] {name});
        }
        return keywordId;
    }

    /**
     * Logs the method entry and input parameters.
     *
     * @param logger the logger
     * @param name the method name
     * @param paramNames the parameter names, if it is null, then needn't to log input parameters
     * @param paramValues the parameter values
     */
    public static void logMethodEntryAndInputParameters(Log logger, String name, String[] paramNames,
            Object[] paramValues) {
        logger.log(Level.DEBUG, String.format("[Entering method %s]", name));
        if (paramNames != null) {
            StringBuilder sb = new StringBuilder("[Input parameters[");
            for (int i = 0; i <= paramNames.length - 1; i++) {
                if (i != 0) {
                    sb.append(", ");
                }
                sb.append(toString(paramNames[i], paramValues[i]));
            }
            sb.append("]]");
            logger.log(Level.DEBUG, sb.toString());
        }
    }

    /**
     * Logs the method exit and output parameters.
     *
     * @param logger the logger
     * @param name the method name
     * @param logParam represents if output parameters need to be log
     * @param value the return value
     */
    public static void logMethodExitAndOutputParameters(Log logger, String name, boolean logParam,
            Object value) {
        logger.log(Level.DEBUG, String.format("[Exiting method %s]", name));
        if (logParam) {
            logger.log(Level.DEBUG, String.format("[Output parameter %s]", toString(value)));
        }
    }

    /**
     * Logs the exception.
     *
     * @param logger the logger
     * @param name the method name
     * @param exception the exception
     */
    public static void logException(Log logger, String name, Throwable exception) {
        StringBuilder sb = new StringBuilder("[Error in method ");
        sb.append(name).append(": details ").append(exception.getMessage()).append("]");
        logger.log(Level.ERROR, exception, sb.toString());
    }

    /**
     * Checks if the value is null.
     *
     * @param name the param name
     * @param value the param value
     * @throws DataSetProcessingException if the value is null
     */
    public static void checkNotNull(String name, Object value) throws DataSetProcessingException {
        if (value == null) {
            throw new DataSetProcessingException(name + " should not be null");
        }
    }

    /**
     * Converts the param value (<code>Object</code>) to a string.
     *
     * @param name the param name
     * @param value the param value
     * @return the string
     */
    private static String toString(String name, Object value) {
        StringBuilder sb = new StringBuilder();
        if (value instanceof Loggable) {
            sb.append(toString(name, (Loggable) value));
        } else if (value instanceof List) {
            sb.append(toString(name, (List<?>) value));
        } else {
            sb.append(name).append(":").append(String.valueOf(value));
        }
        return sb.toString();
    }

    /**
     * Converts the param value (<code>Loggable</code>) value to a string.
     *
     * @param name the param name
     * @param value the param value
     * @return the string
     */
    private static String toString(String name, Loggable value) {
        StringBuilder sb = new StringBuilder();
        if (value == null) {
            sb.append(name).append(":").append(value);
        } else {
            sb.append(name).append(":").append(value.toJSONString());
        }
        return sb.toString();
    }

    /**
     * Converts the param value (<code>List</code>) to a string.
     *
     * @param name the param name
     * @param obj the param value.
     * @return the string.
     */
    private static String toString(String name, List<?> obj) {
        StringBuilder sb = new StringBuilder();

        boolean first = true;
        for (Object element : obj) {
            if (!first) {
                sb.append(", ");
            }
            first = false;
            sb.append(toString(name, element));
        }

        return sb.toString();
    }

    /**
     * Converts the param value (<code>Object</code>) to a string.
     *
     * @param value the param value
     * @return the string
     */
    private static String toString(Object value) {
        if (value instanceof Loggable) {
            return toString((Loggable) value);
        } else if (value instanceof List) {
            return toString((List<?>) value);
        } else {
            return String.valueOf(value);
        }
    }

    /**
     * Converts the param value (<code>Loggable</code>) value to a string.
     *
     * @param value the param value
     * @return the string
     */
    private static String toString(Loggable value) {
        if (value == null) {
            return null;
        } else {
            return value.toJSONString();
        }
    }

    /**
     * Converts the param value (<code>List</code>) to a string.
     *
     * @param obj the param value.
     * @return the string.
     */
    private static String toString(List<?> obj) {
        StringBuilder sb = new StringBuilder();

        boolean first = true;
        for (Object element : obj) {
            if (!first) {
                sb.append(", ");
            }
            first = false;
            sb.append(toString(element));
        }

        return sb.toString();
    }
}
